package com.fsu.database.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Class for managing the database. Developers should use this to
 * interface with the database.
 * @author Bonazza
 *
 */
public class DatabaseManager {

	/**
	 * The Database URL
	 */
	private static String URL = "jdbc:db2://130.184.26.148:446/ZUAF";
	/**
	 * The database driver to be used
	 */
	private static String DRIVER = "com.ibm.db2.jcc.DB2Driver";
	/**
	 * The SQL Database connection objet associated with the 
	 * database instance
	 */
	private static Connection con;
	
	/**
	 * Load the database driver. This must be called 
	 * before any other database related calls can be made.
	 * @throws ClassNotFoundException
	 */
	public static void loadDriver() throws ClassNotFoundException {
		Class.forName(DRIVER);
	}
	
	/**
	 * Connect to the database if not already connected
	 * @param user the database username
	 * @param pass the database password
	 * @throws SQLException
	 */
	public static void connect(String user, String pass) throws SQLException {
		
		if(con == null)
			con = DriverManager.getConnection(URL, user, pass);
	}
	
	/**
	 * Closes the database connection if it is connected and releases it from memory
	 * @throws SQLException
	 */
	public static void disconnect() throws SQLException {
		if(con != null) {
			con.close();
			con = null;
		}
	}
	
	/**
	 * Checks whether or not we are currently connected to the database
	 * @return whether or not we are connected to the database
	 */
	public static boolean isConnected() {
		return con != null;
	}
	
	/**
	 * Retrieve a list of all media in the database
	 * @param comparator an optional Comparator object. If not null,
	 * the resulting list will be sorted based on this Comparator
	 * @return A (potentially sorted) list of all media in the database
	 * @throws SQLException
	 */
	public static List<Media> getMedia(Comparator<Media> comparator) throws SQLException {
		MediaDAO dao = MediaDAO.getInstance(con);
		List<Media> media = dao.getMedia();
		
		if(comparator != null)
			Collections.sort(media, comparator);
		
		return media;
	}
	
	/**
	 * Retrieve a list of all books in the database
	 * @param comparator an optional Comparator object. If not null,
	 * the resulting list will be sorted based on this Comparator
	 * @return A (potentially sorted) list of all books in the database
	 * @throws SQLException
	 */
	public static List<Book> getBooks(Comparator<Book> comparator) throws SQLException {
		MediaDAO dao = MediaDAO.getInstance(con);
		List<Book> books = dao.getBooks();
		
		if(comparator != null)
			Collections.sort(books, comparator);
		
		return books;
	}
	
	/**
	 * Retrieve a list of all dvds in the database
	 * @param comparator an optional Comparator object. If not null,
	 * the resulting list will be sorted based on this Comparator
	 * @return A (potentially sorted) list of all dvds in the database
	 * @throws SQLException
	 */
	public static List<DVD> getDvds(Comparator<DVD> comparator) throws SQLException {
		MediaDAO dao = MediaDAO.getInstance(con);
		List<DVD> dvds = dao.getDvds();
		
		if(comparator != null)
			Collections.sort(dvds, comparator);
		
		return dvds;
	}
	
	/**
	 * Retrieve a list of all orders in the database
	 * @param comparator an optional Comparator object. If not null,
	 * the resulting list will be sorted based on this Comparator
	 * @return A (potentially sorted) list of all orders in the database
	 * @throws SQLException
	 */
	public static List<Order> getOrders(Comparator<Order> comparator) throws SQLException {
		OrderDAO dao = OrderDAO.getInstance(con);
		List<Order> orders = dao.getOrders();
		
		if(comparator != null)
			Collections.sort(orders, comparator);
		
		return orders;
	}
	
	/**
	 * Searches all media for a specified title
	 * @param searchStr the string to search for in the media's properties.
	 * The properties searched depends on the type of media it is.
	 * @param comparator An optional Comparator object. If not null, 
	 * the resulting list will be sorted based on this Comparator
	 * @return a (potentially sorted) list of Media with titles
	 * matching the specified search string
	 * @throws SQLException
	 */
	public static List<Media> searchMedia(String searchStr, Comparator<Media> comparator) throws SQLException{
		List<Media> all = getMedia(comparator);
		List<Media> result = new ArrayList<Media>();
		for(Media media : all) {
			if(media instanceof Book)
			{
				Book book = (Book) media;
				if(book.getTitle().toLowerCase().contains(searchStr.toLowerCase()) ||
						book.getISBN().toUpperCase().contains(searchStr.toUpperCase()) ||
						book.getSubject().toLowerCase().contains(searchStr.toLowerCase())) {
					result.add(book);
				} else {
					for (Author a : book.getAuthors()) {
						if(a.getName().toLowerCase().contains(searchStr.toLowerCase()))
							result.add(book);
					}
				}
			} else {
				DVD dvd = (DVD) media;
				if(dvd.getTitle().contains(searchStr) ||
						dvd.getDirector().toLowerCase().contains(searchStr.toLowerCase()) ||
						dvd.getGenre().toLowerCase().contains(searchStr.toLowerCase())) {
					result.add(dvd);
				} else {
					for (CastMember cm : dvd.getCast()) {
						if(cm.getName().toLowerCase().contains(searchStr.toLowerCase()))
							result.add(dvd);
					}
				}
			}
		}
		
		return result;
	}
	
	/**
	 * Searches all books for a specified string
	 * @param searchStr the string to search for in the books's title, ISBN,
	 * and subject
	 * @param comparator An optional Comparator object. If not null, 
	 * the resulting list will be sorted based on this Comparator
	 * @return a (potentially sorted) list of books with titles, ISBNs, or
	 * subjects matching the specified search string
	 * @throws SQLException
	 */
	public static List<Book> searchBooks(String searchStr, Comparator<Book> comparator) throws SQLException {
		List<Book> books = getBooks(comparator);
		List<Book> result = new ArrayList<Book>();
		for(Book book : books) {
			if(book.getTitle().contains(searchStr) ||
					book.getISBN().toUpperCase().contains(searchStr.toUpperCase()) ||
					book.getSubject().toLowerCase().contains(searchStr.toLowerCase())) {
				result.add(book);
			} else {
				for (Author a : book.getAuthors()) {
					if(a.getName().toLowerCase().contains(searchStr.toLowerCase()))
						result.add(book);
				}
			}
		}
		
		return result;
	}
	
	/**
	 * Searches all DVDs for a specified string
	 * @param searchStr the string to search for in the DVD's title, director,
	 * and genre
	 * @param comparator An optional Comparator object. If not null, 
	 * the resulting list will be sorted based on this Comparator
	 * @return a (potentially sorted) list of DVDs with titles, directors, or
	 * genres matching the specified search string
	 * @throws SQLException
	 */
	public static List<DVD> searchDvds(String searchStr, Comparator<DVD> comparator) throws SQLException {
		List<DVD> dvds = getDvds(comparator);
		List<DVD> result = new ArrayList<DVD>();
		for(DVD dvd : dvds) {
			if(dvd.getTitle().toLowerCase().contains(searchStr.toLowerCase()) ||
					dvd.getDirector().toLowerCase().contains(searchStr.toLowerCase()) ||
					dvd.getGenre().toLowerCase().contains(searchStr.toLowerCase())) {
				result.add(dvd);
			} else {
				for (CastMember cm : dvd.getCast()) {
					if(cm.getName().toLowerCase().contains(searchStr.toLowerCase()))
						result.add(dvd);
				}
			}
		}
		
		return result;
	}
	
	/**
	 * Retrieves the information for a specified user
	 * @param login the username of the target user
	 * @return A User object containing information on a specified user
	 * @throws SQLException
	 */
	public static User getUser(String login) throws SQLException {
		UserDAO dao = UserDAO.getInstance(con);
		return dao.getUser(login);
	}
	
	/**
	 * Checks whether or not a user exists in the database
	 * @param login the username of the target user
	 * @return Whether or not the user exists in the database
	 * @throws SQLException
	 */
	public static boolean userExists(String login) throws SQLException {
		UserDAO dao = UserDAO.getInstance(con);
		return dao.userExists(login);
	}
	
	/**
	 * Adds a new user to the databae. If the user already exists, nothing is done.
	 * @param user the user to be added to the database. This object must be completely
	 * populated with valid data or the insert operation will fail.
	 * @throws SQLException
	 */
	public static void addUser(User user) throws SQLException {
		UserDAO dao = UserDAO.getInstance(con);
		dao.addUser(user);
	}
	
	/**
	 * Updates the information for a registered user
	 * @param oldUser the old information for the user (we only
	 * really care about the username, so that is technically
	 * all that is needed)
	 * @param newUser the new information for the user. This user 
	 * object must be completely populated with valid data.
	 * @throws SQLException
	 */
	public static void updateUser(User oldUser, User newUser) throws SQLException {
		UserDAO dao = UserDAO.getInstance(con);
		dao.updateUser(oldUser, newUser);
	}
	
	public static List<Media> getTop10Sold() throws SQLException {
		MediaDAO dao = MediaDAO.getInstance(con);
		return dao.getTop10Sold();
	}
	
	public static void addBook(Book b) throws Exception {
		MediaDAO dao = MediaDAO.getInstance(con);
		dao.addBook(b);
	}
	
	public static void addDvd(DVD dvd) throws Exception {
		MediaDAO dao = MediaDAO.getInstance(con);
		dao.addDvd(dvd);
	}
}
